﻿/* Copyright (C) 2012 by John Cronin
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:

 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.

 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;

namespace davserv.FSBackend
{
    class FSBackend : Backend
    {
        string path;

        public FSBackend(string Path)
        {
            path = Path;
        }

        FileSystemInfo GetFSI(uri uri)
        {
            string p = path + uri.ToString();

            System.IO.DirectoryInfo di = new System.IO.DirectoryInfo(p);
            if (di.Exists && ((di.Attributes & System.IO.FileAttributes.Directory) == System.IO.FileAttributes.Directory))
                return di;

            System.IO.FileInfo fi = new System.IO.FileInfo(p);
            return fi;
        }

        public override Backend.ByteResponse Get(uri uri)
        {
            FileSystemInfo fsi = GetFSI(uri);

            if ((fsi == null) || (fsi.Exists == false))
                return new ByteResponse { Status = "404" };

            if (fsi is FileInfo)
            {
                FileStream fs = ((FileInfo)fsi).Open(FileMode.Open, FileAccess.Read);
                byte[] buf = new byte[fs.Length];
                fs.Read(buf, 0, buf.Length);
                fs.Close();
                return new ByteResponse { Status = "200", Value = buf };
            }
            else
                return new ByteResponse { Status = "200", Value = new byte[0] };
        }

        public override Backend.Response Put(uri uri, byte[] val, string contenttype, string encoding)
        {
            FileSystemInfo fsi = GetFSI(uri);

            if (fsi is DirectoryInfo)
                return new Response { Status = "405" };
            FileInfo fi = fsi as FileInfo;

            try
            {
                if ((fi.Directory == null) || (fi.Directory.Exists == false))
                    throw new DirectoryNotFoundException();
            }
            catch (DirectoryNotFoundException)
            {
                return new Response { Status = "409" };
            }

            FileStream fs = fi.Open(FileMode.Create, FileAccess.Write);
            fs.Write(val, 0, val.Length);
            fs.Close();

            return new Response { Status = "200" };
        }

        public override Backend.Response MkCol(uri uri)
        {
            throw new NotImplementedException();
        }

        public override Backend.Response Delete(uri uri)
        {
            throw new NotImplementedException();
        }

        public override Backend.StringResponse GetProperty(uri uri, Backend.PropertyName prop_name)
        {
            FileSystemInfo fsi = GetFSI(uri);

            if ((fsi == null) || (fsi.Exists == false))
                return new StringResponse { Status = "404", Value = "<" + prop_name.Name + " xmlns=\"" + prop_name.Namespace + "\"/>" };

            StringResponse val = new StringResponse();

            if (prop_name.Name == "resourcetype")
            {
                val.Status = "200";

                if ((fsi.Attributes & FileAttributes.Directory) == FileAttributes.Directory)
                    val.Value = "<D:resourcetype><D:collection/></D:resourcetype>";
                else
                    val.Value = "<D:resourcetype/>";

                return val;
            }
            else if (prop_name.Name == "displayname")
            {
                val.Status = "200";
                val.Value = "<D:displayname>" + fsi.Name + "</D:displayname>";

                return val;
            }
            else if (prop_name.Name == "creationdate")
            {
                val.Status = "200";
                val.Value = "<D:creationdate>" + Utils.Rfc3339Date(fsi.CreationTimeUtc) + "</D:creationdate>";

                return val;
            }
            else if (prop_name.Name == "getcontentlength")
            {
                val.Status = "200";

                if ((fsi.Attributes & FileAttributes.Directory) == FileAttributes.Directory)
                    val.Value = "<D:getcontentlength>0</D:getcontentlength>";
                else
                    val.Value = "<D:getcontentlength>" + ((FileInfo)fsi).Length.ToString() + "</D:getcontentlength>";

                return val;
            }
            else if (prop_name.Name == "getlastmodified")
            {
                val.Status = "200";
                val.Value = "<D:getlastmodified>" + fsi.LastWriteTimeUtc.ToString("r") + "</D:getlastmodified>";

                return val;
            }
            else
            {
                val.Status = "404";
                val.Value = "<" + prop_name.Name + " xmlns=\"" + prop_name.Namespace + "\"/>";

                return val;
            }
        }

        public override Backend.Response SetProperty(uri uri, Backend.PropertyName prop_name, string val)
        {
            throw new NotImplementedException();
        }

        public override Backend.Response DeleteProperty(uri uri, Backend.PropertyName prop_name)
        {
            throw new NotImplementedException();
        }

        public override Backend.BoolResponse Exists(uri uri)
        {
            FileSystemInfo fsi = GetFSI(uri);
            if ((fsi == null) || (fsi.Exists == false))
                return new BoolResponse { Status = "200", Value = false };
            return new BoolResponse { Status = "200", Value = fsi.Exists };
        }

        public override Backend.BoolResponse IsCollection(uri uri)
        {
            FileSystemInfo fsi = GetFSI(uri);
            if ((fsi == null) || (fsi.Exists == false))
                return new BoolResponse { Status = "200", Value = false };
            return new BoolResponse { Status = "200", Value = ((fsi.Attributes & FileAttributes.Directory) == FileAttributes.Directory) };
        }

        public override Backend.UriListResponse GetChildren(uri uri)
        {
            FileSystemInfo fsi = GetFSI(uri);
            if ((fsi == null) || (fsi.Exists == false))
                return new UriListResponse { Status = "404" };

            if (fsi is DirectoryInfo)
            {
                DirectoryInfo di = fsi as DirectoryInfo;
                UriListResponse resp = new UriListResponse() { Status = "200" };

                foreach (FileSystemInfo child in di.GetFileSystemInfos())
                {
                    string fname = child.FullName.Replace("\\", "/");

                    if (fname.StartsWith(path, StringComparison.CurrentCultureIgnoreCase))
                    {
                        uri c = uri.Parse(fname.Substring(path.Length));
                        resp.Value.Add(c);
                    }
                }

                return resp;
            }

            return new UriListResponse { Status = "200" };
        }
    }
}
